#include "common.h"
#include "Mesh.h"
#include "Triangulator.h"
#include "Sc.h"

using namespace std;
using namespace cg;

Base::Base()
{
        
}

int Base::Generate( const Vec3 &center, const Vec3 &dir, float radius, unsigned int n, int firstVertex )
{       
        assert( n >= 3 );
		assert( radius != 0.0f );

        this->firstIndex = firstVertex; 

        if ( !points.empty() )
                points.clear();

        Vec3 t = Cross( Vec3::X_AXIS, dir );
        if ( t == Vec3::ZERO )
                t = Cross( Vec3::Z_AXIS, dir );

        Vec4 tmp = toVec4( t ); 
        tmp.Normalize();        
        Vec3 normal = tmp.toVec3();             
        tmp *= radius;  

        Mat4 m; 
        RotationMatrix( m, TO_RAD( 360.0f / n ), dir ); 
        
        points.push_back( make_pair( center + tmp.toVec3(), normal ) );        
        for( unsigned int i = 0; i < n; ++i ) {
                tmp = m * tmp;
                normal = tmp.toVec3();
                normal.Normalize();
                points.push_back( make_pair( center + tmp.toVec3(), normal ) );
        }
        return firstVertex + n + 1;
}

void Triangulator::GenerateBasis( const Tree& t )
{
        BranchPoint bp = t.GetBranchPoint( 0 );
        Vec3 dir = Vec3::Y_AXIS;
        dir.Normalize();                
        Base base;
        int vertexIndex = 0;

        int division = 12;

        vertexIndex = base.Generate( bp.position, dir, sqrt( bp.area ), division, vertexIndex );
        basis.insert( make_pair( 0, base ) );
                        
        for( Tree::Segments::const_iterator it = t.segments.begin(); it != t.segments.end(); ++it ) {           
                int branchPointIndex = it->second;
                BranchPoint bp = t.GetBranchPoint( branchPointIndex );
                Vec3 dir = bp.position - t.GetBranchPoint( bp.parent ).position;
                dir.Normalize();                
                Base base;
				assert( bp.area != 0.0f ) ;
                vertexIndex = base.Generate( bp.position, dir, sqrt( bp.area ), division, vertexIndex );
                basis.insert( make_pair( branchPointIndex, base ) );
        }
        // create array of vertices     
        verticesNum = vertexIndex;
        vertices = new Vec3[ vertexIndex ];
        // create array of normals
        normalsNum = verticesNum;
        normals = new Vec3[ normalsNum ];
        int i = 0;
        for( Basis::iterator it = basis.begin(); it != basis.end(); ++it ) {    
                for( Base::Points::iterator it2 = it->second.points.begin(); it2 != it->second.points.end(); ++it2 ) {
                        vertices[ i ] = it2->first;
                        normals[ i ] = it2->second;
                        ++i;
                }
        }
}

void Triangulator::TriangulateBasis( Base firstBase, Base secondBase )
{
        Base& fb =      firstBase.points.size() < secondBase.points.size() ? firstBase : secondBase;
        Base& sb =      firstBase.points.size() < secondBase.points.size() ? secondBase : firstBase;
        unsigned int fb_size = fb.points.size();
        unsigned int sb_size = sb.points.size();
                
        unsigned int c = 0;      
        while( c < fb.points.size() - 1 ) {                             
        
                int indexUp = sb.firstIndex + c;
                
                int indexDown2 = fb.firstIndex;
                if ( ( c + 1 ) != fb_size )
                        indexDown2 = fb.firstIndex + c + 1;

                int indexUp2 = sb.firstIndex;
        
                if ( ( c + 1 ) != sb_size )
                        indexUp2 = indexUp + 1;

                indicesDyn.push_back( fb.firstIndex + c );              
                indicesDyn.push_back( indexUp );
                indicesDyn.push_back( indexDown2 );
                // second triangle
                indicesDyn.push_back( indexDown2 );
                indicesDyn.push_back( indexUp );
                indicesDyn.push_back( indexUp2 );
                ++c;
        }       
}

void Triangulator::Render()
{
        for( Basis::iterator it = basis.begin(); it != basis.end(); ++it ) {
                glBegin( GL_LINE_LOOP );
                for( Base::Points::iterator it2 = it->second.points.begin(); it2 != it->second.points.end(); ++it2 ) {
                        glVertex3f( it2->first.x, it2->first.y, it2->first.z );
                }
                glEnd();
        }
}
//
void Triangulator::GenerateSurface( const Tree& t )
{
        for( Tree::Segments::const_iterator it = t.segments.begin(); it != t.segments.end(); ++it ) {           
                TriangulateBasis( basis[ it->first ], basis[ it->second ] );
        }

        //
        indicesNum = indicesDyn.size();
        indices = new int[ indicesNum ];
        int i = 0;
        for( IndicesDyn::iterator it = indicesDyn.begin(); it != indicesDyn.end(); ++it )
                indices[ i++ ] = *it;
}

void Triangulator::GenerateTextureCoordinates( const Tree& t, float textParamX, float textParamY )
{
        tcoords = new Vec2[ verticesNum ];      
            
        for( Basis::iterator it = basis.begin(); it != basis.end(); ++it ) {            
                int len = it->second.points.size();
                float step = 1.0f / ( len - 1 );

                BranchPoint bp = t.GetBranchPoint( it->first );
                float b = sqrt( bp.area );
                int h = bp.hopsFromBase;

                for( int i = 0; i < len; ++i ) {
                        int index = it->second.firstIndex + i;
                        
                        tcoords[ index ].x = step * i * b * textParamX;
                        tcoords[ index ].y = h * textParamY;         
                }
                //h = h == 0 ? 1 : 0;   
        }
}
        
// compute normal from triangle mesh 
// unused now
void Triangulator::ComputeNormalsFromMesh()
{
        normalsNum = verticesNum;
        normals = new Vec3[ normalsNum ];

        for( int i = 0; i < normalsNum; ++i )
                normals[ i ] = Vec3::ZERO;              

        for( int i = 0; i < indicesNum; i += 3 ) {
                int index1 = indices[ i ];
                int index2 = indices[ i + 1 ];
                int index3 = indices[ i + 2 ];
                Vec3 normal = Cross( vertices[ index2 ] - vertices[ index1 ], 
                                                         vertices[ index3 ] - vertices[ index1 ] );
                normal.Normalize();
                normals[ index1 ] += normal;
                normals[ index2 ] += normal;
                normals[ index3 ] += normal;
        }

        for( int i = 0; i < normalsNum; ++i )
                normals[ i ].Normalize();               
}

//
Mesh* Triangulator::Triangulate( const Tree& t, float textParamX, float textParamY )
{
        GenerateBasis( t );

        GenerateSurface( t );

        //ComputeNormalsFromMesh();

        GenerateTextureCoordinates( t, textParamX, textParamY );

        Mesh* mesh = new Mesh( reinterpret_cast<float*>( vertices ), verticesNum, indices, indicesNum );

        mesh->SetPerVertexNormals( reinterpret_cast<float*>( normals ) );

        mesh->SetPerVertexTextureCoords( reinterpret_cast<float*>( tcoords ) );

        return mesh;
}
